// Author: Bjorn Schobben, Domien Nowicki

#include "Ray.h"
#include <stdio.h>

Ray::Ray(const Vector& newEyepoint, const Vector& newDirection, const World& world): eyepoint(newEyepoint), direction(newDirection)
{
	DdaAlgorithm(world);
}

Ray::~Ray()
{
}

void Ray::DdaAlgorithm(const World& world)
{
	mapX = eyepoint.GetIntX();
	mapY = eyepoint.GetIntY();

	Scalar rayDistX; 
	Scalar rayDistY;

	Scalar dDistX = ((direction.GetY() * direction.GetY()) / (direction.GetX() * direction.GetX()) + 1).Sqrt();
	Scalar dDistY = ((direction.GetX() * direction.GetX()) / (direction.GetY() * direction.GetY()) + 1).Sqrt();

	//X heading north (+1) or south (-1). Y heading west (-1) or east (+1) 
	int xorientation;
	int yorientation;

	bool hit = false; //Intersection with wall?
	bool verticalhit; //True if the wall is hit on the west or east side. False if it's hit on the south or north side.

	//Check if we are heading north or south/west or east. And calculate the initial dX, dY 
	if (direction.GetX() < 0)
    {
		xorientation = -1;
		rayDistX = (eyepoint.GetX() - Scalar(mapX)) * dDistX;
    }
    else
    {
		xorientation = 1;
		rayDistX = (Scalar(mapX+1) - eyepoint.GetX()) * dDistX;
	}

	if (direction.GetY() < 0)
    {
		yorientation = -1;
		rayDistY = (eyepoint.GetY() - Scalar(mapY)) * dDistY;
    }
    else
    {
		yorientation = 1;
		rayDistY = (Scalar(mapY+1) - eyepoint.GetY()) * dDistY;
    }

	// Keep moving along x or y cells (according to distance), only one direction each iteration
	while (hit == 0)
    {                   
		
		if (rayDistX < rayDistY)
		{
			rayDistX += dDistX;
			mapX += xorientation;
			verticalhit = false;
		}
        else
        {
			rayDistY += dDistY;
			mapY += yorientation;
			verticalhit = true;
		}               

	
		// Outside map or hit wall?    
		if (mapY < 0 || mapY >= world.GetMapHeight() || mapX < 0 || mapX >= world.GetMapWidth() || !world.IsEmptyCell(mapX, mapY))
		{
			hit = true;
		}
	}

	// Calculate intersection and side hit
	if (!verticalhit)
	{
		Scalar unitX = mapX + (1-xorientation)/2;	

		distance = (unitX - eyepoint.GetX()) / direction.GetX();
		intersection.SetX( unitX );
		intersection.SetY( eyepoint.GetY() + ((unitX - eyepoint.GetX())*direction.GetY()) / direction.GetX() );

		if (xorientation > 0)
			sideHit = WEST;
		else
			sideHit = EAST;
	}
	else
	{
		Scalar unitY = mapY + (1-yorientation)/2;	

		distance = (unitY - eyepoint.GetY()) / direction.GetY();
		intersection.SetY( unitY );
		intersection.SetX( eyepoint.GetX() + ((unitY - eyepoint.GetY())*direction.GetX()) / direction.GetY() );

		if (yorientation > 0)
			sideHit = SOUTH;
		else
			sideHit = NORTH;
	}
}

const Vector& Ray::GetIntersection() const
{
	return intersection;
}

const Scalar& Ray::GetDistance() const
{
	return distance;
}

enum Ray::SideHit Ray::GetSideHit() const
{
	return sideHit;
}

int Ray::GetMapIntersectionX() const
{
	return mapX;
}

int Ray::GetMapIntersectionY() const
{
	return mapY;
}
